package drds.plus.rule_engine;

import com.google.common.collect.Lists;
import drds.plus.common.model.comparative.ColumnNameToComparativeMapChoicer;
import drds.plus.common.model.comparative.Comparative;
import drds.plus.rule_engine.rule_calculate.*;
import drds.plus.rule_engine.table_rule.ITableRule;
import drds.plus.rule_engine.table_rule.TableRule;
import drds.plus.rule_engine.table_rule.TableRuleSet;
import drds.plus.rule_engine.table_rule.VersionToTableRuleSetMap;
import drds.plus.rule_engine.utils.ComparativeStringAnalyser;
import drds.plus.rule_engine.utils.MatchResultCompare;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * 类名取名兼容老的rule代码<br/>
 * 结合tddl的动态规则管理体系，获取对应{@linkplain ITableRule}
 * 规则定义，再根据sql中condition或者是setParam()提交的参数计算出路由规则 {@linkplain RuleCalculateResult}
 *
 * <pre>
 * condition简单语法： KEY CMP VALUE [:TYPE]
 * 1. KEY： 类似字段名字，用户随意定义
 * 2. CMP： 链接符，比如< = > 等，具体可查看{@linkplain Comparative}
 * 3. VALUE: 对应的值，比如1
 * 4. TYPE: 描述VALUE的类型，可选型，如果不填默认为Long类型。支持: int/long/string/date，可以使用首字母做为缩写，比如i/l/s/d。
 *
 * 几个例子：
 * 1. id = 1
 * 2. id = 1 : long
 * 3. id > 1 and id < 1 : long
 * 4. gmt_create = 2011-11-11 : date
 * 5. id in (1,2,3,4) : long
 * </pre>
 */
@Slf4j
public class Route extends VersionToTableRuleSetMap implements IRoute {

    private RuleCalculate ruleCalculate = new RuleCalculate();


    public void doInit() {

        super.doInit();
    }

    public RuleCalculateResult routes(String tableName, String condition) {
        return routes(tableName, condition, super.getCurrentTableRuleSet());
    }

    public RuleCalculateResult routes(String tableName, String condition, String version) {
        return routes(tableName, condition, super.getTableRuleSet(version));
    }

    public RuleCalculateResult routes(String tableName, String condition, TableRuleSet tableRuleSet) {
        return routes(tableName, generateComparativeMapChoicer(condition), Lists.newArrayList(), tableRuleSet);
    }

    public RuleCalculateResult routes(String tableName, ColumnNameToComparativeMapChoicer columnNameToComparativeMapChoicer, List<Object> args) {
        return routes(tableName, columnNameToComparativeMapChoicer, args, super.getCurrentTableRuleSet());
    }

    public RuleCalculateResult routes(String tableName, ColumnNameToComparativeMapChoicer columnNameToComparativeMapChoicer, List<Object> args, String version) {
        return routes(tableName, columnNameToComparativeMapChoicer, args, super.getTableRuleSet(version));
    }

    public RuleCalculateResult routes(String tableName, ColumnNameToComparativeMapChoicer columnNameToComparativeMapChoicer, List<Object> args, TableRuleSet tableRuleSet) {
        return routes(tableName, columnNameToComparativeMapChoicer, args, tableRuleSet, false);
    }

    public RuleCalculateResult routes(boolean isSelect, String tableName, ColumnNameToComparativeMapChoicer columnNameToComparativeMapChoicer, List<Object> args) throws RuleCalculateDiffrentException {
        return routes(isSelect, tableName, columnNameToComparativeMapChoicer, args, false);
    }

    public RuleCalculateResult routes(boolean isSelect, String tableName, ColumnNameToComparativeMapChoicer columnNameToComparativeMapChoicer, List<Object> args, boolean forceAllowFullTableScan) throws RuleCalculateDiffrentException {
        if (super.getVersionList().size() == 0) {
            throw new RuntimeException("routeWithMulVersion method just support multy version rule_engine,use routes method instead or configuration with multy version style!");

        }

        // 如果只有单套规则,直接返回这套规则的路由结果
        if (super.getVersionList().size() == 1) {
            return routes(tableName, columnNameToComparativeMapChoicer, args, super.getCurrentTableRuleSet(), forceAllowFullTableScan);
        }

        // 如果不止一套规则,那么计算两套规则,默认都返回新规则
        List<String> versions = super.getVersionList();
        if (versions.size() != 2) {
            throw new RuntimeException("not support more than 2 copy rule_engine compare,versions is:" + versions);
        }

        // 第一个排位的为旧规则
        RuleCalculateResult oldResult = routes(tableName, columnNameToComparativeMapChoicer, args, super.getCurrentTableRuleSet(), forceAllowFullTableScan);
        if (isSelect) {
            return oldResult;
        } else {
            // 第二个排位的为新规则
            RuleCalculateResult newResult = routes(tableName, columnNameToComparativeMapChoicer, args, super.getTableRuleSet(super.getVersionList().get(1)), forceAllowFullTableScan);
            boolean compareResult = MatchResultCompare.matchResultCompare(newResult, oldResult);
            if (compareResult) {
                return oldResult;
            } else {
                throw new RuleCalculateDiffrentException();
            }
        }
    }

    /**
     * 返回对应表的defaultDbIndex
     */
    public String getDefaultDataNodeId(String tableName) {
        return getDefaultDataNodeId(tableName, super.getCurrentTableRuleSet());
    }

    /**
     * 返回整个库的defaultDbIndex
     */
    public String getDefaultDataNodeId() {
        return getDefaultDataNodeId(null, super.getCurrentTableRuleSet());
    }

    private RuleCalculateResult routes(String tableName, ColumnNameToComparativeMapChoicer columnNameToComparativeMapChoicer, List<Object> args, TableRuleSet tableRuleSet, boolean forceAllowFullTableScan) {
        if (tableRuleSet != null) {
            TableRule tableRule = tableRuleSet.getTableRule(tableName);
            if (tableRule != null) {
                return ruleCalculate.ruleCalculate(columnNameToComparativeMapChoicer, args, tableRule, true, forceAllowFullTableScan);
            }
        }

        // 不存在规则，返回默认的
        return defaultRoute(tableName, tableRuleSet);
    }

    /**
     * 没有分库分表的逻辑表，返回指定库表
     *
     * @param tableName
     * @param tableRuleSet
     * @return
     */
    private RuleCalculateResult defaultRoute(String tableName, TableRuleSet tableRuleSet) {
        DataNodeDataScatterInfo dataNodeDataScatterInfo = new DataNodeDataScatterInfo();
        // 设置默认的链接库，比如就是groupKey
        dataNodeDataScatterInfo.setDataNodeId(this.getDefaultDataNodeId(tableName, tableRuleSet));
        // 设置表名，同名不做转化
        Map<String, ColumnNameToValueSetMap> tableNames = new HashMap<String, ColumnNameToValueSetMap>(1);
        tableNames.put(tableName, null);
        dataNodeDataScatterInfo.setTableNameToColumnNameToValueSetMapMap(tableNames);

        return new RuleCalculateResult(Arrays.asList(dataNodeDataScatterInfo), new HashMap<String, Comparative>());
    }

    /**
     * 没有分库分表的逻辑表，先从dbIndex中获取映射的库，没有则返回默认的库
     */
    private String getDefaultDataNodeId(String tableName, TableRuleSet tableRuleSet) {
        if (tableRuleSet == null) {
            return super.defaultDataNodeId;
        }

        if (tableName != null) {
            Map<String, String> tableNameToDataNodeIdMap = tableRuleSet.getTableNameToDataNodeIdMap();
            if (tableNameToDataNodeIdMap != null && tableNameToDataNodeIdMap.get(tableName) != null) {
                return tableNameToDataNodeIdMap.get(tableName);
            }
        }
        String dataNodeId = tableRuleSet.getDefaultDataNodeId();
        if (dataNodeId == null) {
            dataNodeId = super.defaultDataNodeId;
        }

        return dataNodeId;
    }

    protected ColumnNameToComparativeMapChoicer generateComparativeMapChoicer(String condition) {
        Map<String, Comparative> comparativeMap = ComparativeStringAnalyser.decodeComparativeString2Map(condition);
        return new SimpleColumnNameToComparativeMapChoicer(comparativeMap);
    }


    public TableRule getTableRule(String tableName) {
        TableRuleSet tableRuleSet = super.getCurrentTableRuleSet();
        TableRule tableRule = null;
        if (tableRuleSet != null) {
            tableRule = tableRuleSet.getTableRule(tableName);
        }
        return tableRule;
    }

    /**
     * 获取所有的规则表
     */
    public List<TableRule> getTableRuleList() {
        List<TableRule> tableRuleList = new ArrayList<TableRule>();
        TableRuleSet tableRuleSet = super.getCurrentTableRuleSet();
        if (tableRuleSet == null) {
            return tableRuleList;
        }
        Map<String, TableRule> tableNameToTableRuleMap = tableRuleSet.getTableNameToTableRuleMap();
        tableRuleList.addAll(tableNameToTableRuleMap.values());
        return tableRuleList;
    }

    public Map<String, String> getDbIndexMap() {
        Map<String, String> result = new HashMap<String, String>();
        TableRuleSet tableRuleSet = super.getCurrentTableRuleSet();
        if (tableRuleSet == null) {
            return result;
        }
        Map<String, String> tableNameToDataNodeIdMap = tableRuleSet.getTableNameToDataNodeIdMap();
        if (tableNameToDataNodeIdMap != null) {
            result.putAll(tableNameToDataNodeIdMap);
        }
        return result;
    }

}
